<template>
    <!-- 支付宝小程序使用$u.getRect()获取组件的根元素尺寸，所以在外面套一个"壳" -->
    <view>
        <view class="u-index-bar">
            <slot/>
            <view v-if="showSidebar" class="u-index-bar__sidebar" @touchstart.stop.prevent="onTouchMove"
                  @touchmove.stop.prevent="onTouchMove"
                  @touchend.stop.prevent="onTouchStop" @touchcancel.stop.prevent="onTouchStop">
                <view v-for="(item, index) in indexList" :key="index" :data-index="index"
                      :style="{zIndex: zIndex + 1, color: activeAnchorIndex === index ? activeColor : ''}"
                      class="u-index-bar__index">
                    {{ item }}
                </view>
            </view>
            <view v-if="touchmove && indexList[touchmoveIndex]" :style="{
				zIndex: alertZIndex
			}" class="u-indexed-list-alert">
                <text>{{ indexList[touchmoveIndex] }}</text>
            </view>
        </view>
    </view>
</template>

<script>
var indexList = function () {
    var indexList = [];
    var charCodeOfA = 'A'.charCodeAt(0);
    for (var i = 0; i < 26; i++) {
        indexList.push(String.fromCharCode(charCodeOfA + i));
    }
    return indexList;
};

/**
 * indexList 索引列表
 * @description 通过折叠面板收纳内容区域,搭配<u-index-anchor>使用
 * @tutorial https://www.uviewui.com/components/indexList.html#indexanchor-props
 * @property {Number String} scroll-top 当前滚动高度，自定义组件无法获得滚动条事件，所以依赖接入方传入
 * @property {Array} index-list 索引字符列表，数组（默认A-Z）
 * @property {Number String} z-index 锚点吸顶时的层级（默认965）
 * @property {Boolean} sticky 是否开启锚点自动吸顶（默认true）
 * @property {Number String} offset-top 锚点自动吸顶时与顶部的距离（默认0）
 * @property {String} highlight-color 锚点和右边索引字符高亮颜色（默认#2979ff）
 * @event {Function} select 选中右边索引字符时触发
 * @example <u-index-list :scrollTop="scrollTop"></u-index-list>
 */
export default {
    name: "u-index-list",
    props: {
        sticky: {
            type: Boolean,
            default: true
        },
        zIndex: {
            type: [Number, String],
            default: ''
        },
        scrollTop: {
            type: [Number, String],
            default: 0,
        },
        offsetTop: {
            type: [Number, String],
            default: 0
        },
        indexList: {
            type: Array,
            default() {
                return indexList()
            }
        },
        activeColor: {
            type: String,
            default: '#2979ff'
        }
    },
    created() {
        // #ifdef H5
        this.stickyOffsetTop = this.offsetTop ? uni.upx2px(this.offsetTop) : 44;
        // #endif
        // #ifndef H5
        this.stickyOffsetTop = this.offsetTop ? uni.upx2px(this.offsetTop) : 0;
        // #endif
        // 只能在created生命周期定义children，如果在data定义，会因为循环引用而报错
        this.children = [];
    },
    data() {
        return {
            activeAnchorIndex: 0,
            showSidebar: true,
            // children: [],
            touchmove: false,
            touchmoveIndex: 0,
        }
    },
    watch: {
        scrollTop() {
            this.updateData()
        }
    },
    computed: {
        // 弹出toast的z-index值
        alertZIndex() {
            return this.$u.zIndex.toast;
        }
    },
    methods: {
        updateData() {
            this.timer && clearTimeout(this.timer);
            this.timer = setTimeout(() => {
                this.showSidebar = !!this.children.length;
                this.setRect().then(() => {
                    this.onScroll();
                });
            }, 0);
        },
        setRect() {
            return Promise.all([
                this.setAnchorsRect(),
                this.setListRect(),
                this.setSiderbarRect()
            ]);
        },
        setAnchorsRect() {
            return Promise.all(this.children.map((anchor, index) => anchor
                .$uGetRect('.u-index-anchor-wrapper')
                .then((rect) => {
                    Object.assign(anchor, {
                        height: rect.height,
                        top: rect.top
                    });
                })));
        },
        setListRect() {
            return this.$uGetRect('.u-index-bar').then((rect) => {
                Object.assign(this, {
                    height: rect.height,
                    top: rect.top + this.scrollTop
                });
            });
        },
        setSiderbarRect() {
            return this.$uGetRect('.u-index-bar__sidebar').then(rect => {
                this.sidebar = {
                    height: rect.height,
                    top: rect.top
                };
            });
        },
        getActiveAnchorIndex() {
            const {
                children
            } = this;
            const {
                sticky
            } = this;
            for (let i = this.children.length - 1; i >= 0; i--) {
                const preAnchorHeight = i > 0 ? children[i - 1].height : 0;
                const reachTop = sticky ? preAnchorHeight : 0;
                if (reachTop >= children[i].top) {
                    return i;
                }
            }
            return -1;
        },
        onScroll() {
            const {
                children = []
            } = this;
            if (!children.length) {
                return;
            }
            const {
                sticky,
                stickyOffsetTop,
                zIndex,
                scrollTop,
                activeColor
            } = this;
            const active = this.getActiveAnchorIndex();
            this.activeAnchorIndex = active;
            if (sticky) {
                let isActiveAnchorSticky = false;
                if (active !== -1) {
                    isActiveAnchorSticky =
                        children[active].top <= 0;
                }
                children.forEach((item, index) => {
                    if (index === active) {
                        let wrapperStyle = '';
                        let anchorStyle = {
                            color: `${activeColor}`
                        };
                        if (isActiveAnchorSticky) {
                            wrapperStyle = {
                                height: `${children[index].height}px`
                            };
                            anchorStyle = {
                                position: 'fixed',
                                top: `${stickyOffsetTop}px`,
                                zIndex: `${zIndex ? zIndex : this.$u.zIndex.indexListSticky}`,
                                color: `${activeColor}`
                            };
                        }
                        item.active = active;
                        item.wrapperStyle = wrapperStyle;
                        item.anchorStyle = anchorStyle;
                    } else if (index === active - 1) {
                        const currentAnchor = children[index];
                        const currentOffsetTop = currentAnchor.top;
                        const targetOffsetTop = index === children.length - 1 ?
                            this.top :
                            children[index + 1].top;
                        const parentOffsetHeight = targetOffsetTop - currentOffsetTop;
                        const translateY = parentOffsetHeight - currentAnchor.height;
                        const anchorStyle = {
                            position: 'relative',
                            transform: `translate3d(0, ${translateY}px, 0)`,
                            zIndex: `${zIndex ? zIndex : this.$u.zIndex.indexListSticky}`,
                            color: `${activeColor}`
                        };
                        item.active = active;
                        item.anchorStyle = anchorStyle;
                    } else {
                        item.active = false;
                        item.anchorStyle = '';
                        item.wrapperStyle = '';
                    }
                });
            }
        },
        onTouchMove(event) {
            this.touchmove = true;
            const sidebarLength = this.children.length;
            const touch = event.touches[0];
            const itemHeight = this.sidebar.height / sidebarLength;
            let clientY = 0;
            clientY = touch.clientY;
            let index = Math.floor((clientY - this.sidebar.top) / itemHeight);
            if (index < 0) {
                index = 0;
            } else if (index > sidebarLength - 1) {
                index = sidebarLength - 1;
            }
            this.touchmoveIndex = index;
            this.scrollToAnchor(index);
        },
        onTouchStop() {
            this.touchmove = false;
            this.scrollToAnchorIndex = null;
        },
        scrollToAnchor(index) {
            if (this.scrollToAnchorIndex === index) {
                return;
            }
            this.scrollToAnchorIndex = index;
            const anchor = this.children.find((item) => item.index === this.indexList[index]);
            if (anchor) {
                this.$emit('select', anchor.index);
                uni.pageScrollTo({
                    duration: 0,
                    scrollTop: anchor.top + this.scrollTop
                });
            }
        }
    }
};
</script>

<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";

.u-index-bar {
    position: relative
}

.u-index-bar__sidebar {
    position: fixed;
    top: 50%;
    right: 0;
    @include vue-flex;
    flex-direction: column;
    text-align: center;
    transform: translateY(-50%);
    user-select: none;
    z-index: 99;
}

.u-index-bar__index {
    font-weight: 500;
    padding: 8 rpx 18 rpx;
    font-size: 22 rpx;
    line-height: 1
}

.u-indexed-list-alert {
    position: fixed;
    width: 120 rpx;
    height: 120 rpx;
    right: 90 rpx;
    top: 50%;
    margin-top: -60rpx;
    border-radius: 24 rpx;
    font-size: 50 rpx;
    color: #fff;
    background-color: rgba(0, 0, 0, 0.65);
    @include vue-flex;
    justify-content: center;
    align-items: center;
    padding: 0;
    z-index: 9999999;
}

.u-indexed-list-alert text {
    line-height: 50 rpx;
}
</style>
